﻿package com.onebyonedesign.air.managers {
	
	import com.onebyonedesign.air.recents.RecentItem;
	import com.onebyonedesign.air.recents.events.RecentItemEvent;
	
	import flash.display.NativeMenu;
	import flash.display.NativeMenuItem;
	import flash.errors.IllegalOperationError;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.filesystem.File;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	
	import mx.resources.ResourceBundle;
	import mx.resources.ResourceManager;
	
	/**
	* Singleton class accessed with static accessor method recents().
	* Assign it a NativeMenu instance and it will add a "Recent Items" submenu to it.
	* To add new items pass a File instance to the addItem() method.
	* @author Devon O.
	*/
	
	[ResourceBundle("app")]
	public class RecentItemManager extends EventDispatcher {
		
		private static var _riManager:RecentItemManager;
		private static var _instantiate:Boolean = false;
		
		private var _parentMenu:NativeMenu;
		private var _maxItems:int = 10;
		
		private var _recentFileArray:Array ;
		
		private var _recentMenu:NativeMenuItem;
		private var _recentMenu1:NativeMenuItem;
		private var _haveRecents:Boolean = false;
		
		private static const XML_FILE:String = "recents.xml";
		
		/**
		 * @private
		 */
		public function RecentItemManager() {
			if (!_instantiate)
				throw new IllegalOperationError("RecentItemManager is a Singleton class and can only be accessed through \"recents\" accessor.");
		}
		
		/**
		 * Method attempts to load the "recents.xml" file containing all recently opened files.
		 */
		public function checkForRecents():void {
			var xmlFile:File = File.applicationStorageDirectory;
			xmlFile = xmlFile.resolvePath(XML_FILE);
			var uLoader:URLLoader = new URLLoader();
			uLoader.addEventListener(Event.COMPLETE, xmlHandler);
			uLoader.addEventListener(IOErrorEvent.IO_ERROR, xmlHandler);
			uLoader.load(new URLRequest(xmlFile.nativePath));
		}
		
		// called when "recents.xml" is either loaded or can't be found.
		private function xmlHandler(event:*):void {
			// if xml file is not found, create an "Empty" recent item
			if (event is IOErrorEvent) {
				_recentFileArray= new Array();
				_recentFileArray = [[new RecentItem("{"+ResourceManager.getInstance().getString('app','phpc.mainmenu.file.empty')+"}")]];
			
			// if xml file is found create a list of RecentItem instances then build the recent items menu
			} else {
				_haveRecents = true;
				var xml:XML = new XML(event.currentTarget.data);
				var itemList:XMLList = xml.item;
				var len:int = itemList.length();
				_recentFileArray= new Array();
				
				for (var i:int = 0; i < len; i++)
					_recentFileArray.push(new RecentItem(itemList[i].title.toString(), itemList[i].path.toString()));
			}
			
			buildMenu();
		}
		
		// creates the recent item menu which will be added to the NativeMenu instance assigned to the manager
		public function buildMenu():void {
			var menu:NativeMenu = new NativeMenu();
			var len:int = _recentFileArray.length;
			
			for (var i:int = 0; i < len; i++) {
				var item:NativeMenuItem = menu.addItem(new NativeMenuItem(_recentFileArray[i].title));
				item.name = i.toString();
				item.addEventListener(Event.SELECT, itemHandler);
			}
			
			if (_haveRecents) {
				menu.addItem(new NativeMenuItem("sep", true));
				var clearItem:NativeMenuItem = menu.addItem(new NativeMenuItem(ResourceManager.getInstance().getString('app','phpc.mainmenu.file.clearrecent')));
				clearItem.addEventListener(Event.SELECT, clearRecentsHandler);
			}

			_recentMenu1 = _parentMenu.addItemAt(new NativeMenuItem("",true), 6);
			_recentMenu = _parentMenu.addSubmenuAt(menu,7, ResourceManager.getInstance().getString('app','phpc.mainmenu.file.openrecent'));
		}
		
		public function rewriteMenu():void {
			_parentMenu.removeItem(_recentMenu);
			buildMenu();
		}
		
		// Called when a recent item is clicked on.
		private function itemHandler(event:Event):void {
			var ind:int = int(event.currentTarget.name);
			
			// Check to see if file exists (user may delete or rename file since last opening it).
			// If it does exist, dispatch RecentItemEvent.RECENT_SELECT to notify object instance that
			// a recent file is opening.
			if (isFile(_recentFileArray[ind].path)) {
				var myEvent:RecentItemEvent = new RecentItemEvent(RecentItemEvent.RECENT_SELECT);
				var item:RecentItem = _recentFileArray.splice(ind, 1)[0];
				_recentFileArray.unshift(item);
				myEvent.filePath = item.path;
				dispatchEvent(myEvent);
			} else {
				removeRecent(_recentFileArray[ind].path);
			}
			
			// recreate the menu and save the recents list to "recents.xml".
			rewriteMenu();
			saveRecents();
		}
		
		// removes all items from list and deletes the recents.xml file.
		private function clearRecentsHandler(event:Event):void {
			clear();
		}
		
		public function clear():void {
			var recentsXML:File = File.applicationStorageDirectory;
			recentsXML = recentsXML.resolvePath(XML_FILE);
			
			if (recentsXML.exists) {
				_haveRecents = false;
				recentsXML.deleteFile();
				_recentFileArray = [[new RecentItem("{"+ResourceManager.getInstance().getString('app','phpc.mainmenu.file.empty')+"}")]];
				rewriteMenu();
			}
		}
		
		// Determine if file exists or not
		private function isFile(filePath:String):Boolean {
			var f:File = new File(filePath);
			if (f.exists) 
				return true;
				
			return false;
		}
		
		// If a recent item proves to be invalid (no longer pointing at a valid file),
		// this removes the item from the list.
		private function removeRecent(filePath:String):void {
			var len:int = _recentFileArray.length;

			for (var i:int = 0; i < len; i++) {
				if (filePath == _recentFileArray[i].path) {
					_recentFileArray.splice(i, 1);
				}
			}
		}
		
		// generates the xml file containing recent items and saves it in application directory
		private function saveRecents():void {
			var recentXML:XML = <recentItems />
			var numItems:int = _recentFileArray.length;
			for (var i:int = 0; i < numItems; i++)
				recentXML.appendChild(<item><title>{_recentFileArray[i].title}</title><path>{_recentFileArray[i].path}</path></item>);

			var xmlFile:File = File.applicationStorageDirectory;
			xmlFile = xmlFile.resolvePath(XML_FILE);
			var outputString:String = '<?xml version="1.0" encoding="utf-8"?>\n';
			outputString += recentXML.toXMLString();
			outputString = outputString.replace(/\n/g, File.lineEnding);
			var outputStream:FileStream = new FileStream();
			outputStream.open(xmlFile, FileMode.WRITE);
			outputStream.writeUTFBytes(outputString);
			outputStream.close();
		}
		
		/**
		 * Static method used to access the singleton instance of the RecentItemManager object.
		 */
		public static function get recents():RecentItemManager {
			if (!_riManager) {
				_instantiate = true;
				_riManager = new RecentItemManager();
				_instantiate = false;
			}
			return _riManager;
		}
		
		/**
		 * Call this after assigning parentMenu and maxItems properties. This will actually create the menu.
		 */
		public function createMenu():void {
			if (!_parentMenu)
				throw new IllegalOperationError("RecentItemManager needs parentMenu property set.");
			
			checkForRecents();
		}
		
		/**
		 * Sets the NativeMenu instance that the recent items submenu will be attached to.
		 * Mostly commonly seen in a "File" menu.
		 */
		public function set parentMenu(value:NativeMenu):void {
			_parentMenu = value;
		}
		
		public function get parentMenu():NativeMenu { return _parentMenu; }
		
		/**
		 * Sets the maximum number of items saved in the Recent Item list.
		 */
		public function set maxItems(value:int):void {
			_maxItems = value;
		}
		
		public function get maxItems():int { return _maxItems; }
		
		/**
		 * Check to see if a file has been recently opened.
		 * 
		 * @param	filePath	The full path to a file to check.
		 * @return 				True if the file is already in the recents list. False, otherwise.
		 */
		public function isRecent(filePath:String):Boolean {
			var len:int = _recentFileArray.length;
			for (var i:int = 0; i < len; i++) {
				if (filePath == _recentFileArray[i].path)
					return true;
			}
			return false;
		}
		
		/**
		 * Adds a file to the list of recently opened.
		 * 
		 * @param	file	File instance.
		 */
		public function addRecent(file:File):void {
			
			if(!isRecent(file.nativePath)){
				var item:RecentItem = new RecentItem(file.name, file.nativePath);
				var len:int = _recentFileArray.length;
			
				_recentFileArray.unshift(item);
			}
			
			if (_recentFileArray.length > _maxItems)
				_recentFileArray.pop();
			
			// if we don't have any recents yet, we do now so pop off the "Empty" recent item.
			if (!_haveRecents) {
				_haveRecents = true;
				_recentFileArray.pop();
			}
			
			rewriteMenu();
			saveRecents();
		}
	}
}